<?php
// +----------------------------------------------------------------------
// | zhanshop-mcp / TestMcpClient.php    [ 2025/8/10 17:44 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2011~2025 zhangqiquan All rights reserved.
// +----------------------------------------------------------------------
// | Author: zhangqiquan <768617998@qq.com>
// +----------------------------------------------------------------------
declare (strict_types=1);

namespace app\console;

use app\provider\AiRewriteClient;
use zhanshop\App;
use zhanshop\client\AichatClient;
use zhanshop\client\McpClient;
use zhanshop\console\Command;
use zhanshop\console\Input;
use zhanshop\console\Output;

class TestAiRewriteClient extends Command
{

    public function configure()
    {
        $this->useDatabase();
    }

    public function execute(Input $input, Output $output)
    {
        $client = new AiRewriteClient();
        $body = $client->rewrite("1)为什么要有协程?

2)客户端的同步和异步
    1.客户端的同步：发送一个请求，等待服务器端的返回。如：请求Redis，等待返回value
        缺点： 每一秒钟客户端请求服务器的数量不会很多。

    2.客户端的异步：不停的提交。


    结论：客户端使用异步，速度远远快于同步。

3)king式四元组：
    1.init 
    2.commit
    3.callback
    4.destroy 

4)服务器端的同步与异步(以增加1000连接为例子)
    (1)同步： 5.6s
        epoll进行读写是否就绪的监听，并且recv和send在一个流程里面，则叫同步。

    (2)异步： 0.8s
        实现：epoll监听到读写事件，包装为任务，扔到线程池中，
            线程池中进行recv与send。

        定义：epoll事件感应和recv、send不在一个线程中，这叫异步。

5)2个口语词
    (1)异步IO： AIO的模式，有数据了直接回调。

    (2)IO异步操作：多线程异步\多线程同步

6)同步/异步 与 协程有什么关系？
    (1)知乎上的回答：用同步的编程方式，异步的。。。

    (2)同步和异步是在形容2者之间的关系。 如果找不到2者，请用：阻塞与非阻塞。


    同步的优点：读完后解析，符合人线性化的思维。

    异步：
        多个客户端连接服务器。
        一个客户端发出一个IO事件：“你好”。
        检测一个IO事件，服务器把这个IO事件push到另外一个线程里面。
        这时，再次发送一个“你好1”。
        又收到。。。多个线程共用一个fd的线程--》脏数据。--》对fd进行加锁--》不利于我们代码写逻辑。

        同步的编程方式，更符合我们的思维，更加的直观。但是同步的性能不高，没有异步的好。

    目的：有没有同步的编程方式，却有异步的性能？

    协程是如何解决这个问题的？

7)目标：
    (1)现在：客户端一下子能提交多个请求。
       慢慢的等待服务器端的返回。--》返回是在另外一个callback处理。
    
    (2)目标：多个请求，看起来是同步的等待返回。 但是却拥有异步的性能。==》如何实现？

8)实现跳转的3种方法(类似于goto的语句，是函数内部跳用)
    1.setjump/longjmp: 跨越函数栈的跳转。 -->c的标准接口
    2.ucontext: 系统上下文的跳转。-->linux的接口
    3.自己用汇编来实现：跳转。--》go的底层实现也是汇编。

    封装为2个原语操作：
        yield： jump-->pos   (commit后，让出cpu，依赖于epoll，到callback函数里面去，callback执行完后，就resume回到执行过程。)
        resume： jump-->back  ==》什么时候调用是由epoll控制的。
        switch:  采用汇编来实现。 用几个寄存器保存上下文。

9)如何实现跳转？
    2个CPU的上下文切换：
        一个CPU，上面保存了16个寄存器。

    线程1在运行。 保存线程1里面寄存器的值(store)，加载线程2里面的值(load)，这就是线程上下文的切换。

    eax,ebx...

10)如何保存寄存器里面的值？

11)协程的出现：
    实现这样一个框架：降低编程难度。同步的写法，异步的性能。 让出CPU，同时依赖于epoll，实现流程的切换。

================

1)封装为协程后的
    从客户端提交请求-->callback函数返回数据,是在一个调度单元里面的。

    epoll时，让出cpu，让epoll检测是否有数据：
        有数据：recv，recv完毕后，切换回来。
        没有：

2)之前：
    主线程一直commit，另外一个线程等待结果的返回。

    现在： 提交请求，让出cpu--》切换到另外一个线程的。

    IO密集型的情况下，协程可以完全替代线程。

    服务器计算结果，协程不能代替线程。不是强计算。 

3)如何定义 ‘协程’ 结构体
    1.cpu_ctx
    2.status(ready,running,defer)
    3.func: 入口函数
    4.arg
    5.stack栈空间: 为什么是必要的? ==》让每一个协程 “独立” 的根本。==》sp指针--》指向的位置 就是栈--》接下来CPU帮助你处理。
        是独立的，而不是共用的。

    6.stack_size

    一个调度器，调度N多个协程。每一个有着不同的状态。
    当前运行着是哪个协程。 多个就绪，多个休眠--》但是只有一个在运行。

4)'调度器'的定义
    1.cur_ctx
    2.set<ready>
    3.set<defer>
    4.epfd

5)
    同步： 1个请求，一个回应。
    异步： 发一个请求，再发一个请求。
    同步写法,异步性能： 发一个请求，让出CPU，跳转到接收的地方。判断IO是否有数据，没有数据，再切换回来。 
        一定是有很多的异步请求，才有必要。

        提交请求的地方，叫做让出CPU，跳到：recv这。。处理完，再跳回来。 ==》其实表面是同步的，底层依然是：异步的。 

6)

   结构。调用和关闭是托管使用的。
   入口函数。
   协程的参数 


   线程： id, NULL, server, port 

   coroutine_create: 分配一个couroutine，并且加入到就绪队列ready里面。 并不是开始运行了，只有调度器调度到此能运行。

   run: 调度器的开始执行。

7)coroutine：协程 
   1.cpu上下文，寄存器组
   2.协程的入口函数
   3.参数
   4.栈大小
   5.处于一个什么状态(就绪N、等待N、休眠N、退出N--》集合)
   6.就绪集合--》一个元素。 队列   --》
   7.等待集合--》一个元素。 队列。（超时时间，则可以用红黑树）
   8.休眠集合--》一个元素。 红黑树 --》    等待某个条件的满足。 -->一次性超出多个。
   

   一个协程，在多个集合里面是完全有可能的。

8)Timer的实现
   最小堆、红黑树、时间轮

9)调度器的定义
    1.当前运行的哪个协程。--》方便我们yield(sched->cur, sched->cur->next)操作。
    2.epfd: 整个协程调度的最原始的动力。 


    全局变量--》加到调度器里。
    每个协程特有的加到协程里面。


10)
    ntyco提供的api。
    schedule_run调度的方法。


    有多个地方调用recv，则切换跳到另外一个recv的地方。 直到找到fd准备就绪了。--》这样让recv函数本身就是异步的。==》同步的recv改为异步的recv。

11)核心点： nty_poll_inner
        将fd加到epoll里面。--》

12)跳转的核心的点:
    nty_coroutine_yield 

13)在recv之前，将fd加入到全局epoll里面。     nty_recv、nty_send、nty_connect、nty_accept
    1.fd--》epoll
    2.yield   ==>


    调度器：master主线程，不属于任何一个coroutine      co1    co2   co3    co4

        向左是yield，，向右是resume


        并不是由线程1到线程2，而是线程1切换到调度器里面。 然后调度器再切换到线程2.


    调度器里面执行的全部是resume函数。

    在协程里面执行的全部是yield函数。


14)没有东西加入到epoll里面，则会阻塞。

   先create，再调用schedule_run, 否则就直接退出了。

15)co被创建出来的时候，一次都没有运行的时候，是如何被第一次运行的，入口点在哪里？
      co->ctx.eip=_exec;


   IO密集型的操作，底层核心是epoll在驱动着调度器调度。

=========
多个IO可读写的话：处理完一个，返回到调度器，调度器再调度下一个。

==========多核
1.借助线程。
    a.所有的线程共用一个调度器;
    b.每个线程一个调度器；

2.借助进程。


=================CPU的粘合
每个CPU上都有一个调度队列。

绑定CPU：这个进程只在这个CPU上执行。

================

其实协程的性能，并不比：epoll+多线程的方式高，毕竟协程内部还是依赖于epoll。

使用最主要的原意是：简单，容易维护！");
        file_put_contents(App::rootPath().'/public/index.html', '<meta charSet="utf-8"/>'."\n\n".$body);
    }
}